﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NBOW
{
    public class Calculator
    {
        double k1 = 1.2;
        double b = 0.8;
        Common com = new Common();
        ConnectDB connect = new ConnectDB();

        #region calculator value

        //cal weight ti word of 1 bug report Brj -- ct3
        private double calculation_wi(double idf_ti, double tf_ti_BRj, double BRj_length, double dl_avg)
        {
            double wi = (idf_ti * tf_ti_BRj * (k1 + 1)) / (tf_ti_BRj + k1 * (1 - b + b * (BRj_length / dl_avg)));
            return wi;
        }

        //cal frequency ti word appears in 1 bug report BRj
        private double calculation_tf_ti_BRj(double f_ti, double max_f)
        {
            return 0.5 + (0.5 * f_ti / max_f);
            // check with
            //return f_ti / max_f;
            //return 1 + Math.Log10(f_ti);
            //return k1 + (1 - k1) * f_ti / max_f;
        }

        // cal inverse document frequency -- ct4
        private double calculation_idf_ti(int N, double df_ti)
        {
            return Math.Log10((double.Parse(N.ToString()) - df_ti + 0.5) / (df_ti + 0.5));
        }

        //cal dl_avg
        public double calculation_dl_avg(DataTable dtAllBug, Dictionary<string, double> dicFrequency, List<int> listColumns)
        {

            List<string> listValueRow = new List<string>();
           
            double d_avg = 0;

            if (dtAllBug != null)
            {
                for (int i = 0; i < dtAllBug.Rows.Count; i++)
                {
                    string str_arraytext = null;
                    for (int j = 0; j < dtAllBug.Columns.Count; j++)
                    {
                        if (listColumns.Contains(j))
                            continue;
                        str_arraytext += dtAllBug.Rows[i][j].ToString() + " ";
                    }
                    listValueRow.Add(str_arraytext);
                }
                // get d_avg
                foreach (string str_row in listValueRow)
                {
                    int int_i = 0;
                    string str_Temp = "";
                    List<string> ListTemp = new List<string>();
                    str_Temp = com.RemoveDuplicateWords(str_row);
                    ListTemp = com.processWordofBug(str_Temp);
                    foreach (string word in ListTemp)
                    {
                        string str_word = word.ToUpper();
                        if (dicFrequency.ContainsKey(str_word))
                            int_i++;
                    }
                    d_avg += int_i;
                }
                d_avg = d_avg / dtAllBug.Rows.Count;
            }
            return d_avg;
        }

        // get appear frequency of word in bug report
        public Dictionary<string, double> GetTf_qi(List<string> tempBug)
        {
            Dictionary<string, double> result = new Dictionary<string, double>();
            foreach (string s1 in tempBug)
            {
                string s = s1.ToUpper();
                if (result.ContainsKey(s))
                {
                    int val = int.Parse(result[s].ToString());
                    result.Remove(s);
                    result.Add(s, ++val);
                    result[s] = result[s]++;
                }
                else
                    result.Add(s, 1);
            }
            return result;
        }

        #endregion

        #region calculator bag and search

        // cal bag
        public Dictionary<string, double> bag(DataTable dtAllBug, Dictionary<string, double> dicFrequency, string path)
        {
            // get empty columns
            List<int> listColumns = new List<int>();
            listColumns = com.disField(dtAllBug); 

            Dictionary<string, double> dic_temp = new Dictionary<string, double>();
            List<string> listRow = new List<string>();
            
            if (dtAllBug != null)
            {
                for (int i = 0; i < dtAllBug.Rows.Count; i++)
                {
                    string str_arraytext = null;
                    for (int j = 0; j < dtAllBug.Columns.Count; j++)
                    {
                        if (listColumns.Contains(j))
                            continue;
                        //listRow.Add(dtAllBug.Rows[i][j].ToString());
                        str_arraytext += dtAllBug.Rows[i][j].ToString() + " ";
                    }
                    listRow.Add(str_arraytext);
                }
                int max_f = 0;
                int N = dtAllBug.Rows.Count;
                double BR_length = dicFrequency.Count;
                foreach (string item in dicFrequency.Keys)
                {
                    if (max_f < int.Parse(dicFrequency[item].ToString()))
                        max_f = int.Parse(dicFrequency[item].ToString());
                }
                double d_avg = calculation_dl_avg(dtAllBug, dicFrequency, listColumns);
                foreach (string item in dicFrequency.Keys)
                {
                    //cal appear frequency of word in all bug report
                    int df_ti = 0; // number bug report contains word in all bug report              
                    foreach (string s in listRow)
                    {
                        if (s.ToUpper().IndexOf(item.ToUpper()) != -1) //contain word in string s
                            df_ti++;
                    }
                    int f_ti = int.Parse(dicFrequency[item].ToString());// number appear of word in all bug report
                    double idf_ti = this.calculation_idf_ti(N, double.Parse(df_ti.ToString())); //cal idf (ti)
                    if (idf_ti < 0)
                        idf_ti = 0;
                    double tf_ti = this.calculation_tf_ti_BRj(double.Parse(f_ti.ToString()), double.Parse(max_f.ToString())); // cal  tf (ti,BR)
                    double wi = this.calculation_wi(idf_ti, tf_ti, BR_length, d_avg); // cal wi by ct4             

                    dic_temp.Add(item, wi);
                }
            }
            return dic_temp;
        }

        // cal Score(q,d)
        public Dictionary<string, double> Score(string str_bug, DataTable dtAllBug,  List<string> listChooseField)
        {
            // read BOW
            Dictionary<string, double> dicBOW = new Dictionary<string, double>();
            dicBOW = com.readBagOfWord();

            // read Frequency
            Dictionary<string, double> dicFre = new Dictionary<string, double>();
            dicFre = com.readFrequencyWord();

            // get empty columns list
             List<int> listColumns = new List<int>();
            listColumns = com.disField(dtAllBug); 

            string str_CurrentBugTemp = "";
            List<string> listCurrentBug = new List<string>(); //list contains current bug

            str_CurrentBugTemp = com.RemoveDuplicateWords(str_bug);//process word duplicate
            listCurrentBug = com.processWordofBug(str_CurrentBugTemp); // process word
           
            // read frequency file
            Dictionary<string, double> dic_bug = new Dictionary<string, double>(); //Dic contains words and number appear in 1 bug
            dic_bug = GetTf_qi(listCurrentBug); //Dic contains word and number appear of word in current bug       

            //Cal appear frequency of word in all bug report
            int df_ti = 0; // number bug report contains word ti in all bug report
            Dictionary<string, double> dicGetTf_d = new Dictionary<string, double>();
            Dictionary<string, double> ListResult = new Dictionary<string, double>();
            List<string> listtemp_bug = new List<string>();
            List<string> listtemp_Remobug = new List<string>();
            string temp_Rebug = "";
            int tf_qi = 0;
            
            // get d_avg
            int N = dtAllBug.Rows.Count;
            double d_avg = 0;
            d_avg = calculation_dl_avg(dtAllBug, dicFre, listColumns);

            #region cal Score
            for (int i = 0; i < dtAllBug.Rows.Count; i++)
            {
                string d = "";
                foreach (string t in listChooseField)
                {
                    d += dtAllBug.Rows[i][t].ToString() + " ";
                }
                int d_length = 0;
                listtemp_bug = com.processWordofBug(d);
                temp_Rebug = com.RemoveDuplicateWords(d);
                listtemp_Remobug = com.processWordofBug(temp_Rebug);
                //number appear of word t in bug (cal |d|) 
                foreach (string t in listtemp_Remobug)
                {
                    if (dicBOW.ContainsKey(t.ToUpper()))
                        d_length++;
                }
                dicGetTf_d = GetTf_qi(listtemp_bug);//dic word and number appear of word in bug report
                double score = 0;
                foreach (string str_ite in listtemp_Remobug)
                {
                    foreach (string item in dic_bug.Keys)
                    {
                        if (str_ite.ToUpper() == item.ToUpper())
                        {
                            df_ti = 0;
                            //define word contains in how many bug.
                            for (int j = 0; j < dtAllBug.Rows.Count; j++)
                            {
                                string s = "";
                                foreach (string t in listChooseField)
                                {
                                    s += dtAllBug.Rows[j][t].ToString();
                                }
                                if (s.ToUpper().IndexOf(item.ToUpper()) != -1) //have word in string s 
                                    df_ti++;
                            }
                            tf_qi = int.Parse(dicGetTf_d[item].ToString()); //number of occurrences of word in bug
                            double idf_qi = calculation_idf_ti(N, double.Parse(df_ti.ToString())); //cal idf (qi)
                            score += calculation_wi(idf_qi, tf_qi, d_length, d_avg); //cal wi by ct4                       
                        }
                    }
                }
                if(ListResult.ContainsKey(d))
                    continue;
                else
                    ListResult.Add(d, score);
            }
            #endregion

            return ListResult;
        }

        #endregion

        #region create vector and compare

        // create vector
        public Dictionary<string, double> Create_Vecto(string str_bug)
        {
            List<string> listCurrentBug = new List<string>(); //list contains current bug
            listCurrentBug = com.processWordofBug(str_bug); // process word

            Dictionary<string, double> dic_bug = new Dictionary<string, double>(); //dic contains word and number occurrences of word in 1 bug
            dic_bug = GetTf_qi(listCurrentBug);   

            Dictionary<string, double> vecto = new Dictionary<string, double>();

            // read BOW
            Dictionary<string, double> dicBOW = new Dictionary<string, double>();
            dicBOW = com.readBagOfWord();

            foreach (string item in dic_bug.Keys)
            {
                foreach (string s in dicBOW.Keys)
                {
                    if (item == s)
                        vecto.Add(item, dicBOW[s]);
                }
            }
            return vecto;  // weight vecto 
        }

        //Compare vector
        public double Compare_Vecto(Dictionary<string, double> X, Dictionary<string, double> Y)
        {
            double sumbp_X = 0; 
            double sumbp_Y = 0; 
            double multip = 0; 
            double cos = 0; 
            List<string> same = new List<string>(); 
            List<double> X_val = new List<double>();
            List<double> Y_val = new List<double>();
            foreach (string x in X.Keys)
            {
                foreach (string y in Y.Keys)
                {
                    if (y == x)
                    {
                        same.Add(x);
                        break;
                    }
                }
            }
            if (same.Count > 1)
            {
                X = Sort(X, same);
                Y = Sort(Y, same);

                foreach (string item in X.Keys)
                {
                    X_val.Add(double.Parse(X[item].ToString()));
                }
                foreach (string item in Y.Keys)
                {
                    Y_val.Add(double.Parse(Y[item].ToString()));
                }

                if (X.Count > Y.Count) 
                {
                    for (int i = 0; i < Y.Count; i++)
                    {
                        multip += X_val[i] * Y_val[i];
                    }
                }
                else
                {
                    for (int i = 0; i < X.Count - 1; i++)
                    {
                        multip += X_val[i] * Y_val[i];
                    }
                }

                for (int i = 0; i < X.Count - 1; i++)
                {
                    sumbp_X += X_val[i] * X_val[i];
                }
                for (int i = 0; i < Y.Count - 1; i++)
                {
                    sumbp_Y += Y_val[i] * Y_val[i];
                }
                cos = multip / (Math.Sqrt(sumbp_X) * Math.Sqrt(sumbp_Y)); 
            }
            return cos;
        }

        private Dictionary<string, double> Sort(Dictionary<string, double> a, List<string> b)
        {
            Dictionary<string, double> temp = new Dictionary<string, double>();
            foreach (string i in a.Keys)
            {
                foreach (string j in b)
                {
                    if (j == i)
                    {
                        temp.Add(i, double.Parse(a[i].ToString()));
                        break;
                    }
                }
            }
            foreach (string i in a.Keys)
            {
                bool check = true;
                foreach (string j in temp.Keys)
                {
                    if (j == i)
                    {
                        check = false;
                        break;
                    }
                }
                if (check == true)
                {
                    temp.Add(i, double.Parse(a[i].ToString()));
                }
            }
            return temp;
        }

        #endregion
    }
}
